﻿#include "serial_file_com.h"
#include <stdio.h>
CSerialFileCom::CSerialFileCom()
{
	m_fileSize = SIZE_MB(128);
	m_baseLBA  = 2048;

	m_bRecordWtTime = true;
	m_access_mod = -1;
	m_pLock = NULL;
#ifndef _WIN32
	m_pLock = new pthread_mutex_t; 		//互斥锁
	pthread_mutexattr_t mutexAttr;
	pthread_mutexattr_init(&mutexAttr);
	pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE);
	pthread_mutex_init((pthread_mutex_t*)m_pLock,&mutexAttr);
	pthread_mutexattr_destroy(&mutexAttr);
#endif

	m_align_buf = NULL;
	m_cache_buf = NULL;
	m_cache_pos   = 0;
	m_cur_offset  = 0;
	m_sec_size    = SIZE_HDD_BLOCK1;
	m_lastRet     = 0;
	m_pFile       = NULL;
    m_pCacheBuf   = new char[4096];
}

CSerialFileCom::~CSerialFileCom()
{

}

int CSerialFileCom::Open(void * file,uint64_t fileLBA, uint32_t fileSize, int acess_mod)
{

	if(NULL == file)
	{
		TRACE_ERR("NO device.\n");
		return ERR_FILE_NOOPEN;
	};
	Lock();
	char file_name[40] = {0};
	strcpy(file_name,(char*)file);

	strcpy(m_storage_file,file_name);
	if(0 == access(file_name,0))
	{
		if(acess_mod == SCSI_RDWR)
		{
			m_pFile = fopen(file_name,"r+");
		}
		else
		{
			m_pFile = fopen(file_name,"w+");
		}
	}
	else
	{  //首次打开
		m_pFile = fopen(file_name,"wb+");
	}

	if( NULL == m_pFile)
	{
		TRACE("Error open %s  errno %d .\n",file_name,errno);
		Lock();
		return ERR_FILE_OPEN;
	}
	m_baseLBA    = fileLBA;
	m_fileSize   = fileSize;
	m_cur_offset = 0;
	m_status     = FILE_NORMAL;
	m_access_mod = acess_mod;

	if(m_bRecordWtTime)
	{
		SetLastWriteTm();
	}
	m_lastRet = 0;
#ifdef _WIN32
	setbuf(m_pFile, m_pCacheBuf);
#else
	setbuffer(m_pFile, m_pCacheBuf,4096);
#endif
	Lock();
	return 0;
}

int CSerialFileCom::ReOpen()
{
	if(NULL != m_pFile)
	{
		fclose(m_pFile);
		m_pFile = NULL;
	}

	return Open(m_storage_file,m_baseLBA,m_fileSize,m_access_mod);
}


int CSerialFileCom::Close()
{
	Lock();

	m_baseLBA    = 0;
	m_fileSize   = 0;
	m_cur_offset = 0;
	m_status     = FILE_CLOSED;
	m_access_mod = SCSI_CLOSED;
	if(NULL != m_pFile)
	{
		fclose(m_pFile);
		m_pFile = NULL;
	}

	Unlock();

	return 0;
}

int CSerialFileCom::Seek(int32_t offset,int whence)
{
    int  ret = 0;
	if(offset < 0)
	{
		goto ERR_OUT;
	}

	if((uint32_t)abs(offset) > m_fileSize )
	{
		goto ERR_OUT;
	}

	if( NULL == m_pFile)
	{
		return ERR_FILE_NOOPEN;
	}

	ret = fseek(m_pFile,offset,whence);
	if(0 != ret )
	{
		return ERR_SEEK_FAIL;
	}
	switch(whence)
	{
	case SCSI_SEEK_END:
		m_cur_offset = m_fileSize - 1;
		break;

	case SCSI_SEEK_CUR:
		if(offset < 0)
		{
			if((uint32_t)abs(offset) > m_cur_offset )
			{
				goto ERR_OUT;
			}
			m_cur_offset = m_cur_offset - abs(offset);
		}
		else
		{
			if(IsOverFileArea(offset))
			{
				goto ERR_OUT;
			}
			m_cur_offset = m_cur_offset + offset;
		}
		break;

	case SCSI_SEEK_SET:

		if(offset < 0)
		{
			goto ERR_OUT;
		}
		else if((uint32_t)offset > m_fileSize )
		{
			goto ERR_OUT;
		}
		else
		{
			m_cur_offset =(uint32_t)offset;
		}
		break;

	default:
		TRACE_ERR("Error param.\n");
		goto ERR_OUT;
		break;
	}
	m_cur_offset = offset;

	return 0;
ERR_OUT:
	return ERR_SEEK_FAIL;
}

int CSerialFileCom::Read(uchar_t * pBuf, uint32_t dataSize, bool bLock)
{
	if( NULL == m_pFile )
	{
		return ERR_NO_DEVICE;
	}

	if( 1 != fread(pBuf,(size_t)dataSize,1,m_pFile))
	{
		TRACE("Read data fail. errno %d \n",errno);
		return ERR_READ_FAIL;
	}
	
	return dataSize;
}

int CSerialFileCom::Write(uchar_t * pBuf, uint32_t dataSize, bool bLock)
{
	if( NULL == m_pFile )
	{
		return ERR_NO_DEVICE;
	}

	if( 1 != fwrite(pBuf,(size_t)dataSize,1,m_pFile))
	{
		return ERR_WRITE_FAIL;
	}
	
	return dataSize;
}

int CSerialFileCom::ReadPos(uint32_t offset, uchar_t * pBuf, uint32_t dataSize, bool bLock)
{
	int ret = 0;
	int nRet= 0;
	int diff = 0;
	if(NULL == m_pFile)
	{
		return 0;
	}
	nRet =  fseek(m_pFile,offset,SEEK_SET);
	if(0 != nRet)
	{
		TRACE("File %s ReadPos data fail. offset %d dataSize %d nRet %d \n",m_storage_file,offset,dataSize,nRet);
		nRet = fseek(m_pFile,offset,SEEK_SET);
		// 两次都定位失败
		if(nRet != 0 ) //尝试使用其它方法
		{
			m_cur_offset = ftell(m_pFile) ;
			diff = m_cur_offset - offset;
			nRet = fseek(m_pFile,diff,SEEK_CUR);
			if(nRet != 0)
			{
				ret = errno;
				ReOpen();
				 nRet = fseek(m_pFile,offset,SEEK_SET);
				 if(nRet != 0)
				{
					TRACE_ERR("SEEK_CUR error destoffset %d  diff %d m_cur_offset %d  errno %d . \n",offset,diff,m_cur_offset,ret);
					return ERR_SEEK_FAIL;
				}
			}
		}
	}
	ret =fread(pBuf,(size_t)dataSize,1,m_pFile);
	if( 1 != ret)
	{
		ret = errno;
		TRACE("File %s ReadPos data fail. offset %d dataSize %d errno %d \n",m_storage_file,offset,dataSize,ret);
		return ERR_READ_FAIL;
	}

	m_cur_offset = offset + dataSize;
	return dataSize;
}

int CSerialFileCom::WritePos(uint32_t offset, uchar_t * pBuf, uint32_t dataSize, bool bLock)
{
	if( NULL == m_pFile )
	{
		return ERR_NO_DEVICE;
	}
	int ret = 0;
	int diff = 0;
	int nRet = 0;
	uint32_t curOffset = 0;

	nRet =  fseek(m_pFile,offset,SEEK_SET);
	if(0 != nRet)
	{
		ret = errno;
		TRACE("File %s ReadPos data fail. offset %d dataSize %d nRet errno %d \n",m_storage_file,offset,dataSize,nRet,ret);
		nRet = fseek(m_pFile,offset,SEEK_SET);
		// 两次都定位失败
		if(nRet != 0 ) //尝试使用其它方法
		{
			m_cur_offset = ftell(m_pFile) ;
			diff = m_cur_offset - offset;
			nRet = fseek(m_pFile,diff,SEEK_CUR);
			if(nRet != 0)
			{
				ReOpen();
				nRet = fseek(m_pFile,offset,SEEK_SET);
				if(nRet != 0)
				{
					TRACE_ERR("SEEK_CUR error destoffset %d  diff %d m_cur_offset %d ret %d . \n",offset,diff,m_cur_offset,ret);
					return ERR_SEEK_FAIL;
				}
			}
		}
	}

	curOffset =  ftell(m_pFile);
	if(curOffset != offset)
	{
		ReOpen();
		nRet = fseek(m_pFile,offset,SEEK_SET);
		if(nRet != 0)
		{
			TRACE_ERR("SEEK_CUR error destoffset %d  diff %d m_cur_offset %d ret %d . \n",offset,diff,m_cur_offset,ret);
			return ERR_SEEK_FAIL;
		}
	}
	if( 1 != fwrite(pBuf,(size_t)dataSize,1,m_pFile))
	{
	    ret = errno;

		TRACE("File %s WritePos data fail. offset %d dataSize %d ret %d  \n",m_storage_file,offset,dataSize,ret);
		return ERR_WRITE_FAIL;
	}

	return dataSize;
}

int CSerialFileCom::WriteCachePos(uint32_t offset, uchar_t * pBuf, uint32_t dataSize, bool bLock)
{
	int ret = dataSize;
	if( NULL == m_pFile )
	{
		return ERR_NO_DEVICE;
	}

	fseek(m_pFile,offset,SEEK_SET);

	if( 1 != fwrite(pBuf,dataSize,1,m_pFile))
	{
		TRACE("File %s WriteCachePos data fail. offset %d dataSize %d \n",m_storage_file,offset,dataSize);
		return ERR_WRITE_FAIL;
	}
	m_cur_offset = offset + dataSize;
	if(m_bRecordWtTime)
	{
		SetLastWriteTm();
	}

	return ret;
}

 int CSerialFileCom::FillTotalSpace()
 {
	 int ret = fseek(m_pFile,m_fileSize-1,SEEK_SET);
	 if(ret < 0)
	 {
		 fclose(m_pFile);
		 return ret;
	 }
	 if(fwrite("1",1,1,m_pFile) != 1)
	 {
		 fclose(m_pFile);
		 return ERR_WRITE_FAIL;
	 }
	 return ret;
 }

 void CSerialFileCom::Flush()
 {
	 if( NULL == m_pFile )
	 {
		 return;
	 }
	 if(NULL != m_pFile)
	 {
		 fflush(m_pFile);
	 }
 }
